#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

from db2.comm.db2_exception import ErrCodeException
from db2.comm.error_code import Db2ErrCode
from common import common
from common.const import CMDResult
from common.logger import Logger
from db2.comm.const import Db2Const
from db2.comm.util.comm_decorators import handle_restore_error
from db2.comm.util.common_util import Db2CommonUtil

LOGGER = Logger().get_logger(filename="db2.log")


class SingleUtil:

    @staticmethod
    def is_single_cluster_when_restore(param_dict):
        """恢复任务判断是否单机"""
        cluster_type = param_dict.get("job", {}).get("targetEnv", {}).get("extendInfo", {}).get("clusterType", "")
        return cluster_type == Db2Const.SINGLE

    @staticmethod
    def execute_restore_db(os_user, db_name, restore_db_cmd, encoding=""):
        LOGGER.info(f"Starting restore database, cmd: {restore_db_cmd}.")
        return_code, out, err = common.execute_cmd(restore_db_cmd, encoding=encoding)
        if return_code != CMDResult.SUCCESS.value:
            LOGGER.error(f"Execute restore database command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise Exception("Execute restore database command failed")
        LOGGER.info(f"Execute restore database command success.")
        # 存在执行restore命令成功，但执行前滚时报”Database "xxx" is not in rollforward pending state on node“的情况
        rollforward_pending_val = Db2CommonUtil.get_rollforward_pending_val_of_db(os_user, db_name)
        if not rollforward_pending_val or rollforward_pending_val.upper() != "DATABASE":
            LOGGER.error(f"The rollforward pending value does not match after executing database restore command, "
                         f"database: {db_name}, rollforward pending: {rollforward_pending_val}.")
            raise Exception("The rollforward pending value does not match after executing database restore command")
        LOGGER.info(f"Execute restore database command success.")

    @staticmethod
    def execute_rollforward_db(rollforward_db_cmd, encoding=""):
        LOGGER.info(f"Starting rollforward database, cmd: {rollforward_db_cmd}.")
        return_code, out, err = common.execute_cmd(rollforward_db_cmd, encoding=encoding)
        if return_code != CMDResult.SUCCESS.value:
            LOGGER.error(f"Execute rollforward database command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise Exception("Execute rollforward database command failed")
        if "The ROLLFORWARD command completed successfully" not in out:
            LOGGER.error("The rollforward database result information is incorrect.")
            raise Exception("The rollforward database result information is incorrect")
        LOGGER.info(f"Execute rollforward database command success.")

    @staticmethod
    @handle_restore_error
    def execute_restore_ts(restore_ts_cmd, encoding=""):
        """执行恢复表空间集命令"""
        LOGGER.info(f"Starting restore tablespaces, cmd: {restore_ts_cmd}.")
        return_code, out, err = common.execute_cmd(restore_ts_cmd, encoding=encoding)
        if "successful" not in out and "成功完成" not in out:
            LOGGER.error(f"Execute restore tablespaces command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise ErrCodeException(Db2ErrCode.FAILED_EXECUTE_COMMAND, *[restore_ts_cmd, out])
        LOGGER.info(f"Execute restore tablespaces command success.")

    @staticmethod
    def check_rollforward_pending_after_exec_restore_ts(os_user, db_name):
        rollforward_pending_val = Db2CommonUtil.get_rollforward_pending_val_of_db(os_user, db_name)
        if not rollforward_pending_val or rollforward_pending_val.upper() != "TABLESPACE":
            LOGGER.error(f"The rollforward pending value does not match after executing tablesapces restore command, "
                         f"database: {db_name}, rollforward pending: {rollforward_pending_val}.")
            raise Exception("The rollforward pending value does not match after executing tablesapces restore command")
        LOGGER.info(f"The rollforward pending value is TABLESPACE after restoring tablespaces.")

    @staticmethod
    def execute_rollforward_ts(rollforward_db_cmd, encoding=""):
        """执行前滚表空间集命令"""
        LOGGER.info(f"Starting rollforward tablespaces, cmd: {rollforward_db_cmd}.")
        return_code, out, err = common.execute_cmd(rollforward_db_cmd, encoding=encoding)
        if return_code != CMDResult.SUCCESS.value:
            LOGGER.error(f"Execute rollforward tablespaces command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise ErrCodeException(Db2ErrCode.FAILED_EXECUTE_COMMAND, *[rollforward_db_cmd, out])
        if "The ROLLFORWARD command completed successfully" not in out and "ROLLFORWARD 命令成功完成" not in out:
            LOGGER.error("The rollforward tablespaces result information is incorrect.")
            raise Exception("The rollforward tablespaces result information is incorrect")
        LOGGER.info(f"Execute rollforward tablespaces command success.")

    @staticmethod
    def execute_offline_backup_ts(offline_backup_ts_cmd, encoding=""):
        """执行离线备份表空间集命令"""
        LOGGER.info(f"Starting executing an offline backup of the tablespaces, command: {offline_backup_ts_cmd}.")
        return_code, out, err = common.execute_cmd(offline_backup_ts_cmd, encoding=encoding)
        if return_code != CMDResult.SUCCESS.value:
            LOGGER.error(f"Execute the offline backup tablespaces command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise Exception("Execute the offline backup tablespaces command failed")
        LOGGER.info(f"Execute the offline backup tablespaces command success.")

    @staticmethod
    def execute_force_ts(force_all_cmd, encoding=""):
        """执行恢复表空间集命令"""
        LOGGER.info(f"Starting restore tablespaces, cmd: {force_all_cmd}.")
        return_code, out, err = common.execute_cmd(force_all_cmd, encoding=encoding)
        if return_code != CMDResult.SUCCESS.value:
            LOGGER.error(f"Execute restore tablespaces command failed. return code: {return_code}, "
                         f"out: {out}, err: {err}.")
            raise Exception("Execute restore tablespaces command failed")
        LOGGER.info(f"Execute restore tablespaces command success.")

    @staticmethod
    def build_table_space_not_exists_message(all_ts, bak_ts_names):
        return ",".join(set(bak_ts_names).difference(set(all_ts)))

    @staticmethod
    def build_table_space_abnormal_message(all_ts_dict, bak_ts_names):
        state = "state"
        return ",".join((f"{k}:{v.get(state)}" for k, v in all_ts_dict.items()
                         if k in bak_ts_names and v.get("state") != Db2Const.NORMAL_STATE))
